package com.paradogs.common.utils;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.paradogs.common._cfg.Cfg;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.function.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

/**
 * @author: yumi
 * @date: 2024/10/12  22:00
 * @Description: 配置表数据加载
 * @ps: 全局和单个的数据结构加载，可以在这里解析时先转成 json，再用 jsonObject 转成对象，这样不用 filter 一直考虑
 */
@Slf4j
@Component
public class CfgUtils {

    public static final Map<String, Function<String, Object>> typeHandler = new LinkedHashMap<>();
    public static final String SEPARATOR_LEVEL_1 = "=";
    public static final String SEPARATOR_LEVEL_2 = ";";
    static {
        typeHandler.put("number", v -> Integer.valueOf(v));
        typeHandler.put("number[]", v -> Arrays.stream(v.split(SEPARATOR_LEVEL_2)).map(s -> Integer.valueOf(s)).toArray(Integer[]::new));
        typeHandler.put("number[][]", v -> Arrays.stream(v.split(SEPARATOR_LEVEL_2)).map(s -> Arrays.stream(s.split(SEPARATOR_LEVEL_1)).mapToInt(ss -> Integer.valueOf(ss)).toArray()).toArray(Integer[][]::new));
        typeHandler.put("int", v -> Integer.valueOf(v));
        typeHandler.put("int[]", v -> Arrays.stream(v.split(SEPARATOR_LEVEL_2)).map(s -> Integer.valueOf(s)).toArray(Integer[]::new));
        typeHandler.put("int[][]", v -> {
            if (StringUtils.isBlank(v)) return new Integer[0][0];
            String[] split1 = v.split(SEPARATOR_LEVEL_2);
            int len1 = split1.length;
            int len2 = Arrays.stream(split1).mapToInt(s -> s != null ? s.split(SEPARATOR_LEVEL_1).length : 0).max().getAsInt();
            Integer[][] result = new Integer[len1][len2];
            for (int i = 0; i < split1.length; i++) {
                String[] split2 = split1[i].split(SEPARATOR_LEVEL_1);
                for (int k = 0; k < split2.length; k++) {
                    result[i][k] = Integer.valueOf(split2[k]);
                }
            }
            return result;
        });
        typeHandler.put("string", v -> v);
        typeHandler.put("string[]", v -> Arrays.stream(v.split(SEPARATOR_LEVEL_2)).toArray(String[]::new));
        typeHandler.put("string[][]", v -> {
            if (StringUtils.isBlank(v)) return new String[0][0];
            String[] split1 = v.split(SEPARATOR_LEVEL_2);
            int len1 = split1.length;
            int len2 = Arrays.stream(split1).mapToInt(s -> s != null ? s.split(SEPARATOR_LEVEL_1).length : 0).max().getAsInt();
            String[][] result = new String[len1][len2];
            for (int i = 0; i < split1.length; i++) {
                String[] split2 = split1[i].split(SEPARATOR_LEVEL_1);
                for (int k = 0; k < split2.length; k++) {
                    result[i][k] = String.valueOf(split2[k]);
                }
            }
            return result;
        });
        typeHandler.put("double", v -> Double.valueOf(v));
        typeHandler.put("double[]", v -> Arrays.stream(v.split(SEPARATOR_LEVEL_2)).mapToDouble(s -> Double.valueOf(s)).toArray());
        typeHandler.put("double[][]", v -> {
            if (StringUtils.isBlank(v)) return new Double[0][0];
            String[] split1 = v.split(SEPARATOR_LEVEL_2);
            int len1 = split1.length;
            int len2 = Arrays.stream(split1).mapToInt(s -> s != null ? s.split(SEPARATOR_LEVEL_1).length : 0).max().getAsInt();
            Double[][] result = new Double[len1][len2];
            for (int i = 0; i < split1.length; i++) {
                String[] split2 = split1[i].split(SEPARATOR_LEVEL_1);
                for (int k = 0; k < split2.length; k++) {
                    result[i][k] = Double.valueOf(split2[k]);
                }
            }
            return result;
        });
        typeHandler.put("int_kv", v -> Arrays.stream(v.split(SEPARATOR_LEVEL_2)).collect(Collectors.toMap(s -> Integer.valueOf(s.split(SEPARATOR_LEVEL_1)[0]), s -> Integer.valueOf(s.split(SEPARATOR_LEVEL_1)[1]))));
    }

    /** 加载配置表数据 */
    public static void loadCfgData() {
        String f = null;
        try (ZipInputStream zipInputStream = new ZipInputStream(CfgUtils.class.getClassLoader().getResourceAsStream("_cfg/data.bin"))) {
            ZipEntry zipEntry;
            // 解压每个 .json 文件
            while ((zipEntry = zipInputStream.getNextEntry()) != null) {
                // 读取字符串
                byte[] buffer = new byte[1024];
                int length;
                StringBuffer sb = new StringBuffer();
                while ((length = zipInputStream.read(buffer)) > 0) {
                    sb.append(new String(buffer, 0, length));
                }

                // 找到 Cfg 对应的 Class 类
                String fileName = zipEntry.getName(); f = fileName;
                String cfgName = fileName.split("\\.")[0];
                Class<?> cfgClass = Class.forName("com.paradogs.common._cfg.Cfg");
                Class<?>[] declaredClasses = cfgClass.getDeclaredClasses();
                Class<?> targetClass = null;
                for (Class<?> declaredClass : declaredClasses) {
                    if (declaredClass.getName().split("\\$")[1].equals(cfgName)) {
                        targetClass = declaredClass;
                        break;
                    }
                }

                // 解析 Json 数据
                List<JSONArray> jsonArrays = JSON.parseArray(sb.toString(), JSONArray.class);
                String[] columnNameArray = jsonArrays.get(0).toArray(String.class);
                String[] columnTypeArray = jsonArrays.get(1).toArray(String.class);
                jsonArrays = jsonArrays.subList(2, jsonArrays.size()); // 过滤掉列名项和类型

                // 反射创建对应数据的 Class 对象
                Constructor constructor = targetClass.getConstructors()[0];
                for (JSONArray jsonArray : jsonArrays) {

                    if (!jsonArray.get(0).equals(-1)) {
                        // 加载配置表数据
                        Object[] columnParameters = IntStream.range(0, columnNameArray.length)
                                .mapToObj(i -> new AbstractMap.SimpleEntry<Integer, Object>(i, jsonArray.get(i)))
                                .filter(entry -> StringUtils.isNotBlank(columnNameArray[entry.getKey()]) && !columnNameArray[entry.getKey()].startsWith("$"))
                                .map(entry -> entry.getValue() == null ?
                                        entry.getValue() :
                                        typeHandler.get(columnTypeArray[entry.getKey()])
                                                .apply(entry.getValue().toString()))
                                .toArray();

                        Object o = constructor.newInstance(columnParameters);
                        Field field_dataMap = targetClass.getDeclaredField("dataMap");
                        field_dataMap.setAccessible(true);
                        Map dataMap = (Map) field_dataMap.get(null);
                        Field field_idKey = o.getClass().getDeclaredField(columnNameArray[0]);
                        field_idKey.setAccessible(true);
                        dataMap.put(field_idKey.get(o), o);

                    } else {
                        // 加载设置 -1 项
                        Object[] columnParameter = IntStream.range(0, columnNameArray.length)
                                .mapToObj(i -> new AbstractMap.SimpleEntry<Integer, Object>(i, jsonArray.get(i)))
                                .filter(entry -> StringUtils.isNotBlank(columnNameArray[entry.getKey()]) && columnNameArray[entry.getKey()].startsWith("$"))
                                .map(entry -> entry.getValue())
                                .toArray();
                        Field field_$kv = targetClass.getDeclaredField("$kv");
                        field_$kv.setAccessible(true);
                        field_$kv.set(null, field_$kv.getType().getConstructors()[0].newInstance(columnParameter));
                    }

                }
            }
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("Not found [Cfg] class: com.paradogs.common._cfg.Cfg");
        }catch (FileNotFoundException e) {
            throw new RuntimeException("Not found [data.bin]: classpath:_cfg/data.bin");
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (Exception e) {
            log.error("[{}]Load config error: {}", f, e.getMessage());
            throw e;
        }
        log.info("Config data loading success ...");
        log.info("{}", Cfg.Shop.list());
    }

//    @Override
//    public void run(ApplicationArguments args) throws Exception {
//        CfgUtils.loadCfgData();
//        log.info("Load config data success ...");
//    }
}
